package com.citic.topview.app.filter;

import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.citic.topview.common.security.Principal;
import com.citic.topview.system.util.ShiroUtils;

/**
 * https://jinnianshilongnian.iteye.com/blog/2025656
 * @author magupe
 */
public class SessionControlFilter extends AccessControlFilter{

	public Logger logger = LoggerFactory.getLogger(SessionControlFilter.class);
	
	private static final String url = "http://localhost:8686/topview/a/login"; //踢出后到的地址
	
    private SessionManager sessionManager;
    
    private Cache<String, Deque<Serializable>> cache = null;
    
	public void setSessionManager(SessionManager sessionManager) {
		this.sessionManager = sessionManager;
	}

	public void setCacheManager(CacheManager cacheManager) {
		this.cache = cacheManager.getCache("shiro-session-manager");
	}

	@Override
    protected String getName() {
    	return "sessionControlFilter";
    }
    
	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
		//logger.info("SessionControlFilter --> isAccessAllowed");
		return false;
	}

	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		//logger.info("SessionControlFilter --> onAccessDenied");
		
		Subject subject = getSubject(request, response);
        if(!subject.isAuthenticated() && !subject.isRemembered()) {
            return true;
        }
        
        Session session = subject.getSession();
        Principal principal =  ShiroUtils.getPrincipal(subject);
        Serializable sessionId = session.getId();
        Deque<Serializable> deque = cache.get(principal.getId() + "");
        if(deque == null || deque.size() == 0) {
            deque = new LinkedList<Serializable>();
        }
        if(!deque.contains(sessionId) && session.getAttribute("isLogout") == null) { // 如果队列里没有此sessionId，且用户没有被踢出；放入队列
            deque.push(sessionId);
        }
        cache.put(principal.getId() + "", deque);
        
        
        // 如果队列里的sessionId数超出最大会话数，开始踢人
        while(deque.size() > 1) {
            Serializable sid = null;
            //sid = deque.removeFirst(); // 如果踢出后者
            sid = deque.removeLast(); // 否则踢出前者
            if(sid == sessionId) {
            	sid = deque.removeLast();
            }
                
            try {
                Session sess = sessionManager.getSession(new DefaultSessionKey(sid));
                if(sess != null) {
                    // 设置会话的kickout属性表示踢出了
                	sess.setAttribute("isLogout", true);
                }
            } catch (Exception e) { }
        }
        if (session.getAttribute("isLogout") != null) { // 如果被踢出了，直接退出，重定向到踢出后的地址
            try {
                subject.logout();
            } catch (Exception e) { }
            
            if(!principal.isMobileLogin()) {
                saveRequest(request);
                WebUtils.issueRedirect(request, response, url);
                return false;
            }
        }
        
		return true;
	}

}
